package com.cloud.submit.autoconfigure;


import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * <p> ClassName : LockMethodAop </p>
 * <p>Description : Lock 拦截器(AOP),重复提交(redis方案)  </p>
 * @author : JiangWenJie  
 * @since: 2021-02-01 14:53
 */
@Slf4j
@Aspect
@Configuration
public class LockMethodAop {

	@Autowired
	private RedisTemplate<String, String> lockRedisTemplate;


	@Pointcut("execution(* com..*.*Controller.*(..))")
	public void logsAspect() {
	};


	/**
	 * 前置通知
	 * @param joinPoint  切点
	 */
	@Before("logsAspect()")
	public void before(JoinPoint joinPoint) throws Exception {
		Signature st = joinPoint.getSignature();// 切面点得到签名
		Object classObj = joinPoint.getTarget().getClass(); //获取目标对象对应的类名
		Object[] args = joinPoint.getArgs(); // 得到参数列表
		StringBuffer reStr = new StringBuffer();
		if (st instanceof MethodSignature) { // 判断是否是方法签名
			MethodSignature mst = (MethodSignature) st;
			Method method = mst.getMethod(); // 得到方法名
			reStr.append("类名称："+classObj.toString()+",方法名称：" + method.getName());
			StringBuffer paramStr = new StringBuffer();
			if (args != null && args.length > 0) {
				paramStr.append("参数列表：");
				for (int i = 0; i < args.length; i++) {
					paramStr.append("【"+args[i]+"】");
				}
			}
			log.info(reStr.toString()+"=="+paramStr.toString());
		}

	}



	@Around("execution(public * *(..)) && @annotation(com.cloud.submit.autoconfigure.CacheLock)")
	public Object interceptor(ProceedingJoinPoint pjp) {
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		Method method = signature.getMethod();
		CacheLock lock = method.getAnnotation(CacheLock.class);
		if (StringUtils.isEmpty(lock.prefix())) {
			throw new BusinessException(ResultCodeConstant.RESULT_FAIL, "网络异常请求稍后再试!");
		}
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		//锁的key
		String lockKey = getLockKey(pjp,request);
		try {
			//向redis换成set值如果true表示获到了锁  false表示没有获取锁   重复提交锁默默过期时间是5秒 CacheParam注解一定要加入参数
			final Boolean success = lockRedisTemplate.opsForValue().setIfAbsent(lockKey, "1");
			if (!success) {
				throw new BusinessException(ResultCodeConstant.RESULT_FAIL_REPETITION_SUBMIT, "您的手速过快，请休息下!");
			}

			//特殊情况特殊处理，例如秒杀活动 设置说的过期时间为1秒， CacheParam注解一定要加入活动id 针对当前活动加锁
			if ( lock.prefix().equals("a:v:a:wac") || lock.prefix().equals("a:v:c:d")) {
				//设置说的过期时间为1秒
				lockRedisTemplate.expire(lockKey, 1, lock.timeUnit());
			} else {
				//设置说的过期时间为5秒(重复提交处理默认值)
				lockRedisTemplate.expire(lockKey, lock.expire(), lock.timeUnit());
			}

			try {
				//执行业务逻辑
				return pjp.proceed();
			} catch (Throwable e) {
				log.error(e.getMessage(), e);
				throw new BusinessException(ResultCodeConstant.RESULT_FAIL, "服务异常，请稍后再试!");
			}

		} finally {
			// TODO 如果演示的话需要注释该代码;实际应该放开
			if ( !lock.prefix().equals("a:v:a:wac") && !lock.prefix().equals("a:v:c:d")) {
				//最后业务逻辑执行完毕后删除锁
				lockRedisTemplate.delete(lockKey);
			}
		}
	}


	/**
	 * 获取自定义注解上面的值
	 * @param pjp 参数
	 * @return String 获取lock key
	 */
	public static String getLockKey(ProceedingJoinPoint pjp,HttpServletRequest request) {
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		Method method = signature.getMethod();
		CacheLock lockAnnotation = method.getAnnotation(CacheLock.class);
		final Object[] args = pjp.getArgs();
		final Parameter[] parameters = method.getParameters();
		StringBuilder builder = new StringBuilder();
		// 默认解析方法里面带 CacheParam 注解的属性,              如果没有尝试着解析实体对象中的
		for (int i = 0; i < parameters.length; i++) {
			final CacheParam annotation = parameters[i].getAnnotation(CacheParam.class);
			if (annotation == null) {
				continue;
			}
			builder.append(lockAnnotation.delimiter()).append(args[i]);
		}
		// 如果没有尝试着解析实体对象中的
		if (StringUtils.isEmpty(builder.toString())) {
			final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
			for (int i = 0; i < parameterAnnotations.length; i++) {
				final Object object = args[i];
				final Field[] fields = object.getClass().getDeclaredFields();
				for (Field field : fields) {
					final CacheParam annotation = field.getAnnotation(CacheParam.class);
					if (annotation == null) {
						continue;
					}
					field.setAccessible(true);
					builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object));
				}
			}
		}
		String header = request.getHeader("Authorization");
		if(header!=null&&!"".equals(header)){
			builder.append(header);
		}
		return lockAnnotation.prefix() + ":" + Md5Utils.MD5(builder.toString());
	}

}
